#include "engine_impl.hh"
#include "command_option.hh"
#include "module_manager.hh"
#include "../corelib/logger/writer.hh"
#include "../corelib/logger/file_appender.hh"
#include "../corelib/logger/console_appender.hh"
#include "../corelib/logger/appender.hh"
#include "../corelib/util/string_util.hh"
#include "frame_delayer.hh"
#include "daemon.hh"
#include "../corelib/util/allocator.hh"
#include <list>
#include <string>
#include <iostream>
#include <memory>

using namespace kratos::engine;
using namespace kratos::corelib;

static bool debug_run = false;

#ifndef WIN32
#include <unistd.h>
#include <sys/types.h>
#include <signal.h>
#include <fstream>
static void signalHandler(int sig) {
    if (sig == SIGQUIT || sig == SIGTERM) {
        debug_run = false;
    }
}
#else
static void signalHandler(int sig) {}
#endif // WIN32

using Engines = std::list<std::shared_ptr<EngineImpl>>;

int debug_main(int argc, const char** argv) {
    // To daemon!
    if (CommandOptionsRef.isStartAsService()) {
        if (!util::daemonize(signalHandler, signalHandler)) {
            std::cerr << "Start daemon failed" << std::endl;
            return -1;
        }
    }
    // debug mode log
    auto appenderType = CommandOptionsRef.getAppenderType();
    if (appenderType == kratos::corelib::AppenderType::File) {
        auto& fileAppender = dynamic_cast<FileAppender&>(loggerRef.selectAppender(AppenderType::File));   
        loggerRef.setLevel(Level::verbose);
        loggerRef.formatter().parse("[%y/%m/%d %H:%M:%S:%s]");
        fileAppender.start("engine.debug.log");
    } else if (appenderType == kratos::corelib::AppenderType::Console) {
        loggerRef.selectAppender(AppenderType::Console);
        loggerRef.setLevel(Level::verbose);
        loggerRef.formatter().parse("[%y/%m/%d %H:%M:%S:%s]");
    }
    // start root engine
    std::shared_ptr<EngineImpl> rootEngine(new EngineImpl());
    if (!rootEngine->startAsRoot()) {
        return -1;
    }
    //
    // start multiple engine for debug mode
    // all engine run in same process
    //
    auto& configNames = CommandOptionsRef.getConfigFileNames();
    Engines engines;
    engines.emplace_back(rootEngine);
    for (auto& name : configNames) {
        std::shared_ptr<EngineImpl> engine(new EngineImpl());
        engines.emplace_back(engine);
        // append module
        engine->getModuleManager().addToWhite(StringUtil::getFileName(name));
        if (!engine->start(name)) {
            engine->stop();
            for (auto& deadEngine : engines) {
                deadEngine->stop();
            }
            // start engine failed
            std::cerr << "Start engine in debug mode failed, name: " << name << std::endl;
            return -1;
        }
    }
    debug_run = true;
    logverb << "All engine now in debug mode and started" << logend;
    while (debug_run) {
        FrameDelayer frameDelayer(CommandOptionsRef.getFrame());
        auto now = TimeUtil::getMillionSecondsTimestampOS();
        frameDelayer.beginFrame(now);
        for (auto& engine : engines) {
            engine->debug_loop(now);
        }
        frameDelayer.endFrame();
    }
    for (auto& deadEngine : engines) {
        deadEngine->stop();
    }
    logverb << "All engine in debug mode stopped" << logend;
    return 0;
}

int engine_main(int argc, const char** argv) {
    EngineImpl engine;
    // to daemon!
    if (CommandOptionsRef.isStartAsService()) {
        if (!engine.startAsDaemon()) {
            std::cerr << "Start daemon failed" << std::endl;
            return -1;
        }
    }
    // start engine
    if (!engine.start()) {
        return -1;
    }
    return 0;
}

int main(int argc, const char** argv) {
    try {
        try {
            if (!CommandOptionsRef.load(argc, argv)) {
               return false;
            }
        } catch (std::exception& e) {
            std::cerr << "Exception:" << e.what() << std::endl;
            return false;
        }
    } catch (std::exception& e) {
        std::cerr << "Command line error:" << e.what() << std::endl;
        return -1;
    }
    if (CommandOptionsRef.isDebug()) {
        std::cout << "Starts all engine with debug mode..." << std::endl;
        return debug_main(argc, argv);
    } else {
        return engine_main(argc, argv);
    }
}
